package model

import (
	"database/sql/driver"
	"fmt"
	"net/url"
	"strings"
	"time"
	"yunj/pkg/global"
	"yunj/pkg/yunj/util"
)

type AdminAuth struct {
	// 主键
	Key                  string         `gorm:"primaryKey;type:varchar(64);not null;uniqueIndex:uk;column:key;comment:权限唯一标识" json:"key" form:"key"`
	ParentKey            string         `gorm:"type:varchar(64);not null;default:'';column:parent_key;comment:父级key" json:"parent_key" form:"parent_key"`
	ParentKeys           StrSliceJson   `gorm:"type:json;column:parent_keys;comment:所有的父级key" json:"parent_keys" form:"parent_keys"`
	Name                 string         `gorm:"type:varchar(64);not null;default:'';column:name;comment:名称" json:"name" form:"name"`
	FullName             string         `gorm:"type:varchar(500);not null;default:'';column:full_name;comment:完整名称，父级名称拼接" json:"full_name" form:"full_name"`
	Desc                 string         `gorm:"type:varchar(200);not null;default:'';column:desc;comment:描述" json:"desc" form:"desc"`
	Type                 uint8          `gorm:"type:tinyint unsigned;not null;default:0;column:type;comment:类型。0常规、11侧边栏菜单、22顶部菜单" json:"type" form:"type"`
	Icon                 string         `gorm:"type:varchar(64);not null;default:'';column:icon;comment:图标class" json:"icon" form:"icon"`
	RequestMode          uint8          `gorm:"type:tinyint unsigned;not null;default:0;column:request_mode;comment:请求方式。0无，11内部路由，22外部地址" json:"request_mode" form:"request_mode"`
	PageOpen             uint8          `gorm:"type:tinyint unsigned;not null;default:0;column:page_open;comment:页面打开方式。0无、11tab子页面、22popup子页面、33new新标签页" json:"page_open" form:"page_open"`
	RequestUrl           RequestUrl     `gorm:"type:varchar(200);not null;default:'';column:request_url;comment:请求地址" json:"request_url" form:"request_url"`
	RequestMethod        string         `gorm:"type:varchar(8);not null;default:'';column:request_method;comment:请求method" json:"request_method" form:"request_method"`
	RequestRequireParams MapSliceJson   `gorm:"type:json;column:request_require_params;comment:请求必要参数" json:"request_require_params" form:"request_require_params"`
	Sort                 uint16         `gorm:"type:smallint unsigned;not null;default:0;column:sort;comment:排序。默认0" json:"sort" form:"sort"`
	IsSystem             uint8          `gorm:"type:tinyint unsigned;not null;default:0;column:is_system;comment:是否为系统。0否，1是" json:"is_system" form:"is_system"`
	IsDemo               uint8          `gorm:"type:tinyint unsigned;not null;default:0;column:is_demo;comment:是否为系统demo。0否，1是" json:"is_demo" form:"is_demo"`
	CreatedAt            *time.Time     `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at,omitempty" form:"created_at"`
	UpdatedAt            *time.Time     `gorm:"column:updated_at;autoUpdateTime;comment:更新时间" json:"updated_at,omitempty" form:"updated_at"`
	State                uint8          `gorm:"type:tinyint unsigned unsigned;not null;default:11;column:state;comment:11 正常 | 22 回收站 | 33 已删除" json:"state" form:"state"`
	isParseRequestisUrl  bool           `gorm:"-" json:"-"`              // 是否解析过request_url
	requestUrlPath       string         `gorm:"-" json:"-"`              // 配置的url不携带参数的路径
	requestUrlParams     map[string]any `gorm:"-" json:"-"`              // 配置的url中携带的参数
	SubAuths             []*AdminAuth   `gorm:"-" json:"sub_auths"`      // 子权限
	SubMenuAuths         []*AdminAuth   `gorm:"-" json:"sub_menu_auths"` // 子菜单权限
	MenuLevel            uint8          `gorm:"-" json:"menu_level"`     // 菜单等级
}

type RequestUrl string

// Value 实现 driver.Valuer 接口
// 将自定义类型的值转换为数据库可以接受的值
func (j RequestUrl) Value() (dbValue driver.Value, err error) {
	if j == "" {
		dbValue = ""
		return
	}
	if strings.HasPrefix(string(j), "http") {
		dbValue = []byte(j)
		return
	}
	adminEntrance := global.Config.GetAppAdminEntrance()
	if strings.HasPrefix(string(j), adminEntrance) {
		dbValue = []byte(strings.TrimPrefix(string(j), adminEntrance))
	} else {
		dbValue = []byte(string(j))
	}
	return
}

// Scan 实现 sql.Scanner 接口
// 将数据库中的值扫描到自定义类型
func (j *RequestUrl) Scan(value interface{}) (err error) {
	var str string
	switch v := value.(type) {
	case []byte:
		str = string(v)
	case string:
		str = v
	default:
		str, err = util.ToString(value)
		if err != nil {
			return fmt.Errorf("failed to string RequestUrl value: %v", value)
		}
	}
	if str == "" {
		*j = ""
		return
	}
	if strings.HasPrefix(str, "http") {
		*j = RequestUrl(str)
		return
	}
	adminEntrance := global.Config.GetAppAdminEntrance()
	if !strings.HasPrefix(str, adminEntrance) {
		str = adminEntrance + str
	}
	*j = RequestUrl(str)
	return
}

// 解析配置的url
func (m *AdminAuth) parseRequestisUrl() {
	if m.isParseRequestisUrl {
		return
	}
	if m.RequestUrl == "" {
		m.isParseRequestisUrl = true
		return
	}

	// 解析URL
	u, err := url.ParseRequestURI(string(m.RequestUrl))
	if err != nil {
		global.Logger.WithField("auth", m).Errorf("后台权限%s解析配置的url: %v", m.Key, err)
		m.isParseRequestisUrl = true
		return
	}
	m.requestUrlPath = u.Path
	// 判断是否有查询参数
	if u.RawQuery != "" {
		m.requestUrlParams = make(map[string]any)
		params := u.Query()
		// 遍历所有参数
		for key, values := range params {
			var value any
			if len(values) == 0 {
				value = nil
			} else {
				value = values[0]
			}
			m.requestUrlParams[key] = value
		}
	}
	m.isParseRequestisUrl = true
}

// 获取配置的url不携带参数的路径
func (m *AdminAuth) GetRequestUrlPath() string {
	if !m.isParseRequestisUrl {
		m.parseRequestisUrl()
	}
	return m.requestUrlPath
}

// 获取配置的url中携带的参数
func (m *AdminAuth) GetRequestUrlParams() *map[string]any {
	if !m.isParseRequestisUrl {
		m.parseRequestisUrl()
	}
	return &m.requestUrlParams
}

// 初始化子权限
func (m *AdminAuth) InitSubAuths(auths []*AdminAuth) {
	if m.SubAuths == nil {
		m.SubAuths = []*AdminAuth{}
	}
	for _, auth := range auths {
		if auth.ParentKey == m.Key {
			m.SubAuths = append(m.SubAuths, auth)
		}
	}
}

// 初始化子菜单权限
func (m *AdminAuth) InitSubMenuAuths(auths []*AdminAuth) {
	if m.SubMenuAuths == nil {
		m.SubMenuAuths = []*AdminAuth{}
	}
	for _, auth := range auths {
		if auth.ParentKey == m.Key {
			m.SubMenuAuths = append(m.SubMenuAuths, auth)
		}
	}
}

// 初始化菜单等级
func (m *AdminAuth) InitMenuLevel(level uint8) {
	m.MenuLevel = level
	subMenuLevel := level + 1
	for _, subAuth := range m.SubMenuAuths {
		subAuth.InitMenuLevel(subMenuLevel)
	}
}
